package org.xjc.utils;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Date;

/**
 * 字符串转Date
 * DateUtils.parse(time, DateUtils.TimeFormat.DATE_TIME_L_C_B);
 *
 */
public final class DateUtils {
    private static final DateTimeFormatter DEFAULT_DATETIME_FORMATTER;
    private static final DateTimeFormatter DEFAULT_DATE_FORMATTER;
    private static final DateTimeFormatter DEFAULT_TIME_FORMATTER;

    private DateUtils() {
    }

    public static Date parseDateTime(String dateTimeStr) {
        return asDate(LocalDateTime.parse(dateTimeStr, DEFAULT_DATETIME_FORMATTER));
    }

    public static Date parseTime(String timeStr) {
        return asDate(LocalDateTime.parse(timeStr, DEFAULT_TIME_FORMATTER));
    }

    public static Date parseDate(String dateStr) {
        return asDate(LocalDateTime.parse(dateStr, DEFAULT_DATE_FORMATTER));
    }

    public static Date parse(String timeStr, TimeFormat timeFormat) {
        return asDate(LocalDateTime.parse(timeStr, timeFormat.formatter));
    }

    public static Date parse(String timeStr, String format) {
        return asDate(LocalDateTime.parse(timeStr, DateTimeFormatter.ofPattern(format)));
    }

    public static Date parse(String timeStr, String format, ZoneId zoneId) {
        return asDate(LocalDateTime.parse(timeStr, DateTimeFormatter.ofPattern(format)), zoneId);
    }

    public static Date parse(String timeStr, TimeFormat timeFormat, ZoneId zoneId) {
        return asDate(LocalDateTime.parse(timeStr, timeFormat.formatter), zoneId);
    }

    public static String formatTime(LocalDateTime time) {
        return DEFAULT_TIME_FORMATTER.format(time);
    }

    public static String formatDateTime(LocalDateTime dateTime) {
        return DEFAULT_DATETIME_FORMATTER.format(dateTime);
    }

    public static String formatDate(LocalDateTime date) {
        return DEFAULT_DATE_FORMATTER.format(date);
    }

    public static String formatTime(Date time) {
        return DEFAULT_TIME_FORMATTER.format(asLocalDateTime(time));
    }

    public static String formatDateTime(Date dateTime) {
        return DEFAULT_DATETIME_FORMATTER.format(asLocalDateTime(dateTime));
    }

    public static String formatDate(Date date) {
        return DEFAULT_DATE_FORMATTER.format(asLocalDateTime(date));
    }

    public static String format(LocalDateTime time, TimeFormat format) {
        return format.formatter.format(time);
    }

    public static String format(Date time, TimeFormat format) {
        return format.formatter.format(asLocalDateTime(time));
    }

    public static String format(Date time, String format) {
        return DateTimeFormatter.ofPattern(format).format(asLocalDateTime(time));
    }

    public static String format(Date time, String format, ZoneId zoneId) {
        return DateTimeFormatter.ofPattern(format).format(asLocalDateTime(time, zoneId));
    }

    public static String format(Date time, TimeFormat format, ZoneId zoneId) {
        return format.formatter.format(asLocalDateTime(time, zoneId));
    }

    public static String getCurrentDateTime() {
        return DEFAULT_DATETIME_FORMATTER.format(LocalDateTime.now());
    }

    public static String getCurrentDateTime(TimeFormat timeFormat) {
        return timeFormat.formatter.format(LocalDateTime.now());
    }

    public static Long getDiffUseDay(Date startDate, Date endDate) {
        Instant start = startDate.toInstant();
        Instant end = endDate.toInstant();
        return Duration.between(start, end).toDays();
    }

    public static Long getDiffUseMilSecond(Date startDate, Date endDate) {
        Instant start = startDate.toInstant();
        Instant end = endDate.toInstant();
        return Duration.between(start, end).toMillis();
    }

    public static Long getDiffUseSecond(Date startDate, Date endDate) {
        Instant start = startDate.toInstant();
        Instant end = endDate.toInstant();
        return Duration.between(start, end).getSeconds();
    }

    public static Long getDiffUseMinute(Date startDate, Date endDate) {
        Instant start = startDate.toInstant();
        Instant end = endDate.toInstant();
        return Duration.between(start, end).toMinutes();
    }

    public static Long getDiffUseMonth(Date startDate, Date endDate) {
        Instant start = startDate.toInstant();
        Instant end = endDate.toInstant();
        return ChronoUnit.MONTHS.between(start, end);
    }

    public static Long getDiffUseYear(Date startDate, Date endDate) {
        Instant start = startDate.toInstant();
        Instant end = endDate.toInstant();
        return ChronoUnit.YEARS.between(start, end);
    }

    public static String getTimeFromSecond(Double second) {
        int h = (new Double(second / 3600.0D)).intValue();
        int m = (new Double((second - (double)(h * 3600)) / 60.0D)).intValue();
        int s = second.intValue() % 60;
        String strHour = h == 0 ? "" : String.format("%d小时", h);
        String strMinute = h == 0 && m == 0 ? "" : String.format("%d分", m);
        String strSecond = String.format("%d秒", s);
        return (strHour + strMinute + strSecond).trim();
    }

    public static Date asDate(LocalDate localDate) {
        return Date.from(localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant());
    }

    public static Date asDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    public static Date asDate(LocalDate localDate, ZoneId zoneId) {
        return Date.from(localDate.atStartOfDay().atZone(zoneId).toInstant());
    }

    public static Date asDate(LocalDateTime localDateTime, ZoneId zoneId) {
        return Date.from(localDateTime.atZone(zoneId).toInstant());
    }

    public static LocalDate asLocalDate(Date date) {
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDate();
    }

    public static LocalDateTime asLocalDateTime(Date date) {
        return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    public static LocalDate asLocalDate(Date date, ZoneId zoneId) {
        return Instant.ofEpochMilli(date.getTime()).atZone(zoneId).toLocalDate();
    }

    public static LocalDateTime asLocalDateTime(Date date, ZoneId zoneId) {
        return Instant.ofEpochMilli(date.getTime()).atZone(zoneId).toLocalDateTime();
    }

    static {
        DEFAULT_DATETIME_FORMATTER = TimeFormat.DATE_TIME_L_C_B.formatter;
        DEFAULT_DATE_FORMATTER = TimeFormat.DATE_L.formatter;
        DEFAULT_TIME_FORMATTER = TimeFormat.TIME_C.formatter;
    }

    public static enum TimeFormat {
        PURE_DAY("dd"),
        PURE_YEAR("yyyy"),
        PURE_MONTH("MM"),
        PURE_MINUTE("mm"),
        PURE_HOUR("HH"),
        PURE_SECOND("ss"),
        DATE_L("yyyy-MM-dd"),
        DATE_CN("yyyy年MM月dd日"),
        DATE_S("yyyy/MM/dd"),
        DATE_DS("yyyy\\MM\\dd"),
        DATE_N("yyyyMMdd"),
        TIME_N("HHmmss"),
        TIME_C("HH:mm:ss"),
        TIME_CN("HH时mm分ss秒"),
        DATE_TIME_L_C_B("yyyy-MM-dd HH:mm:ss"),
        DATE_TIME_CN("yyyy年MM月dd日 HH时mm分ss秒"),
        DATE_TIME_N("yyyyMMddHHmmss"),
        DATE_TIME_N_T("yyyyMMdd'T'HHmmss"),
        DATE_TIME_S_C_B("yyyy/MM/dd HH:mm:ss"),
        DATE_TIME_DS_C_B("yyyy\\MM\\dd HH:mm:ss"),
        DATE_TIME_N_C_B("yyyyMMdd HH:mm:ss"),
        DATE_TIME_MS_L_C_B("yyyy-MM-dd HH:mm:ss.SSS"),
        DATE_TIME_MS_S_C_B("yyyy/MM/dd HH:mm:ss.SSS"),
        DATE_TIME_MS_DS_C_B("yyyy\\MM\\dd HH:mm:ss.SSS"),
        DATE_TIME_MS_N_C_B("yyyyMMdd HH:mm:ss.SSS");

        private transient DateTimeFormatter formatter;

        private TimeFormat(String pattern) {
            this.formatter = DateTimeFormatter.ofPattern(pattern);
        }
    }
}